#include <filesystem>
#include <fstream>
#include <iostream>
#include <regex>

#include "template_helper.h"

namespace
{

std::string readFromFile(const std::string& filename)
{
    namespace fs = std::filesystem;
    const fs::path filePath = fs::path(Common::templateBaseDirectory())
        / fs::path(filename);
    std::ifstream file(filePath.string());
    if (file.is_open())
    {
        return std::string(
            (std::istreambuf_iterator<char>(file)),
            std::istreambuf_iterator<char>()
        );
    }
    throw std::runtime_error("Failed to open file: " + filename);
}

void processFile(
    std::string& content,
    const std::unordered_map<std::string, std::string>& arguments
)
{
    // Этап 1: объединение составных частей в общий шаблон.
    // [[ filename.html ]]
    static const std::regex linkRegex("\\[\\[\\s*(\\S+)\\s*\\]\\]");
    std::smatch match;
    // TODO : Можно оптимизировать, проходя при каждой итерации от места
    // предыдущей вставки.
    while (std::regex_search(content.cbegin(), content.cend(), match, linkRegex))
    {
        const std::string link = match[1];
        const std::string replacement = readFromFile(link);
        content.replace(match.position(), match.length(), replacement);
    }

    // Этап 2: вставка в общий шаблон частей из перечня аргументов.
    // [[# argName ]]
    static const std::regex argumentsRegex("\\[\\[#\\s*(\\S+)\\s*\\]\\]");
    while (std::regex_search(content.cbegin(), content.cend(), match, argumentsRegex))
    {
        const std::string argumentKey = match[1];
        const auto argumentIt = arguments.find(argumentKey);
        if (arguments.cend() == argumentIt)
        {
            // Если аргумент не задан, вместо ссылки на него оставляем пустоту.
            content.replace(match.position(), match.length(), "");
            continue;
        }

        const auto link = argumentIt->second;
        const std::string replacement = readFromFile(link);
        content.replace(match.position(), match.length(), replacement);
    }
}

} // namespace

namespace Common
{

std::string& templateBaseDirectory()
{
    static std::string templateBaseDirectoryValue = "templates";
    return templateBaseDirectoryValue;
}

std::string combineTemplates(
    const std::string& filename,
    const std::unordered_map<std::string, std::string>& arguments
)
{
    std::string content = readFromFile(filename);
    processFile(content, arguments);
    return content;
}

} // namespace Common
